using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;

namespace MvvmBase.ViewModel
{
    public abstract class JobQueueViewModelBase : INotifyPropertyChanged
    {
        private readonly ObservableCollection<ModelJobEntry> _jobQueue = new ObservableCollection<ModelJobEntry>();

        public bool IsBusy
        {
            get
            {
                return _jobQueue.Any(j => j.JobMode == ModeJobMode.Normal);
            }
        }

        public bool IsSmallBusy
        {
            get
            {
                return _jobQueue.Any(j => j.JobMode == ModeJobMode.Low);
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        private void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }

        protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyPath)
        {
            if (propertyPath == null)
                throw new ArgumentNullException("propertyPath");

            try
            {
                var memberExpression = (MemberExpression)propertyPath.Body;
                OnPropertyChanged(memberExpression.Member.Name);
            }
            catch (InvalidCastException)
            {
                throw new ArgumentException("propertyPath must be path to the property");
            }
        }

        protected ModelJobWithParameter<TResult> CreateNewJob<TResult>(ModeJobMode mode = ModeJobMode.Normal)
        {
            return new ModelJobWithParameter<TResult>(_jobQueue, mode);
        }

        protected ModelJob CreateNewJob(ModeJobMode mode = ModeJobMode.Normal)
        {
            return new ModelJob(_jobQueue, mode);
        }

        protected void SubmitOnJobCollectionChanged()
        {
            _jobQueue.CollectionChanged += JobQueueOnCollectionChanged;
        }

        private void JobQueueOnCollectionChanged(object sender,
            NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs)
        {
            RaisePropertyChanged(() => IsBusy);
            RaisePropertyChanged(() => IsSmallBusy);
        }

        public virtual void CancelAllJobs()
        {
            foreach (ModelJobEntry jobEntry in _jobQueue)
            {
                jobEntry.Cancel();
            }
        }
    }
}